Introduction

FutureLearn is an online open course platform in various fields and subjects. The cyber security course is presented by researchers from Newcastle University’s School of Computing Science. It is not just a video-based course, it has sections such as articles, discussions and quizzes. This course is opened in certain periods and many people benefit from this course.

Implementation of the CRISP-DM

1. Business Understanding

Businesses are making effort to achieve an edge with its content marketing program. Showing an article’s reading time to each of article can have a profound and positive effect on reader engagement levels. The first benefit is that users to help them choose the right article for the right amount of available time they have. Another advantage is that estimated reading time impacts engagement metrics seriously. Lastly, it helps to make analyzes according to certain groups.

Reading time is calculated based on the number of words read by the average human per minute. Generally, this average value per minute is between 200 and 250 words. Therefore, the total number of words in the article is divided by this determined average.

However, scientific articles are not like blog posts. Reading time varies according to one’s knowledge, age, country even article subject .For example, professionals can read articles more quickly than students or older people read slower. Hence, we are not making an accurate estimate by using the conventional calculation method.

In this study, the data will be analyzed and the article reading time will be examined in which groups and how it changes. Then, a regression model using these features will be developed that can predict article reading time on a per-person basis.

Business objectives

The following sections will focus on:

    1. Does the time spent on these articles differ according to certain groups ?
    1. How effective are the articles in the course ?
    1. What is the average read time for each article ?
    1. Can we estimate the reading time per person ?

Data Mining Goals

The business objectives is defined, it’s time to translate it into a data mining reality

  • Explore and find features one of run data sets
  • Build predictive model with 2 min RMSE

2. Data Understanding

We will gather, describe and explore the data to make sure it fits the business goal. There are 6 different course periods(runs) and the data is stored in different files and there are 53 in total. It can cause confusion and inefficiency during analysis. We must store and use them in a structured way in our analysis. For this, we transformed our files in a structured way using regex. The function is implemented in libs/globals.R file.

Every run includes 8 data sets as the followings:

However, run 1 and 2 does not include video.stats and also run 1 does not include team.members data.

Gathering and Describing Data

To achieve our business goal , we will use step.activity and enrolments datasets.

Step Activity Summary

The description of each column/variable can be seen below:

  • learner_id: learner unique id
  • step: step id
  • week_number: week number
  • step_number: step low id
  • first_visited_at: first visited time with timezone
  • last_completed_at: completed time with timezone

Enrolment Summary

  • learner_id: learner unique id
  • enrolled_at: enrolment datetime
  • unenrolled_at: unerolment datetime
  • role: role
  • fully_participated_at:
  • purchased_statement_at:
  • gender:
  • country:
  • detected_country:
  • age_range:
  • highest_education_level:
  • employment_status:
  • employment_area:

Other data are as follows;

class_diagram

Data Quality

Before start the analysis part, we should check data quality. First of all, step column in step.activity is numeric. We should convert it to string column. Column transform operations were performed in the munge/01-Transform-Datatype.R file.

Since the step column is numeric, the values 2.2 to 2.20 appear as 2.2. We should fix this first, then convert categorical column. This operation was implemented in munge/02.Fix-StepID-StepActivity.R

In enrollment data set, there are many missing values in age_range, country, employment_area. There are 2 columns for country information. One is the country name given by the user, and the other is the country name determined by the system.

Before proceeding to the exploratory analysis, some feature engineering steps were applied to provide a more comfortable visualization on the data.

New columns are added as following:

  • completed (step_activity) : it is a boolean column that represents whether completed or not
  • is_unenroll (enrolments) : it is a boolean column that represents whether undo the enrollment or not

This operations implemented in munge/03-FeatureEngineering.R

And also, to analyze for all step activity, all files should be merged. So we need to add a new column called run_id to all our data and merge the data. This operation was implemented in munge/04-MergeDataFrames.R

Data Exploratory Anaylsis

run_1_step_activity_count.png

run_1_step_activity.png

run_3_step_activity.png

Firstly, it should be noted in the graph that the step steps are not sequential. The most striking part of the graph is that approximately 750 students did not complete step 1.1. The number of completing the step starts with 1500. After 10 steps, this number varies between 500 and 750 for the rest of the course. In addition, the number of participation in step 3.18 is quite low.

all_step_activity.png

When we look at the graphics for each run, we see that there is a similar pattern. However, In the last steps of Run 1, it seems that the rate of non-completion is higher than the others and the overall number of enrollment is also quite high.

When looked carefully, there are 2 spaces in Run 3. Whereas, a gap was occured in our first chart only at step 3.18. And also these gaps were formed in the runes other than Run 1. It raises suspicion that there may be any missing step. Let’s check it.

step_activity %>%
  group_by(run_id) %>%
  summarise(n_distinct(step))

As we thought, there are missing and extra steps. Because of these different steps, we can misinterpret our data.

3. Data Preparation

Feature Engineering

External Data - Continents

Country based visualizations can also be difficult to interpret as there are many country names. For this, continent data that mapped countries from outside was found and merged with enrollment data.

External Data - Step Activity Metadata

We will create a map list which includes names, types and step_id.

head(run_3.step.metadata)

Then, we will merge with step activity data frames.

df.filtered <- step_activity %>% dplyr::filter(!run_id %in% c(1,2)) # run 1  and run 2 are not same structure
df.filtered <-  merge(x= df.filtered, y = run_3.step.metadata, by.x = "step", by.y="step_id", all.x = TRUE)

Calculating Time Difference

In order to achieve our business goal, we need to calculate the total elapsed time. We can find difference time between first_visited_at and last_completed_at.Since some records do not have a last time date, we will specify their value as 0.

It was stated in one of the business objective that the reading date would be estimated. It will be better in terms of user experience to estimate this estimate in half-minute intervals instead of seconds. We need to round the data in seconds to numbers such as 6.30 minutes, 7 minutes. The name of this newly created column will be called elapsed_time.

These operations were implemented in munge/03-FeatureEngineering.R

Filter Data Frame

We only need to filter records that have a last completed date.


df.filtered  <- df.filtered  %>% filter(diff_second != 0)

4. Data Understanding

As we know, CRISP-DM is not a linear process. To make sure it fits the business and data mining goal, we can go back to data understanding.

There are html files attached with details about the course in data files. These html files contain the names and step types of all the steps. We can use them to find missing and extra steps. And also, we need step types(article, video, etc) in our analysis.

Data Exploratory Anaylsis

run3_step_activity_with_type.png

This bar chart represents the number of participation of each step with their step type.

Boxplot of Elapsed time for each step

filtered_run_elapsed_time_boxplot_by_step.png

First of all, since there are too many steps, it is difficult to determine which step the points belong to. However, it is clear that there are many outliers in the data. It might be better to see them on the box plot.

Boxplot of elapsed time for each type

filtered_run_elapsed_time_boxplot.png

Some users may have left the site open during some steps and left the computer. So we may be seeing these outliers. It seems difficult to interpret the graphs because there are outliers, so we should clean these outliers. The interquartile range is common method used to find outliers in data. Basically, we can implement this method to clean our outliers.

On the other hand, in order to make a demographic interpretation, we need to combine our data with the enrolment data set.

5. Data Preparation

Filter Steps

In the remaining analysis, analysis will be made on the articles. Therefore, it is necessary to filter the data and retrieve only the articles.

df.article <- df.filtered  %>% filter(step_type == "ARTICLE")
unique(df.article$step)

Outlier Detection

When the data is analyzed, we see that the outliers are quite large. It seems that they spend more than 10 hours with steps, but such a thing is not possible. To avoid this issue, it should be defined a threshold for these values then filtered it.

threshold <- 30 * 60 # 30 minute

df.article <- df.article %>% filter(diff_second < threshold)

Only this way of filtering does not remove outliers from the data. An outlier detection method needs to be applied. IQR method will be used in this study. In ProjectTemplate structure, lib folder is good place to store external function. So, our outlier detection method which IQR was implemented in lib/helpers.R file.

Importantly, instead of calculating an outlier over the entire data directly, we will calculate an outlier for each step.

outliers <- df.article  %>% group_by(step) %>% summarise(val=helper.iqr(diff_second)) %>% summarise(min_outlier=min(val))
head(outliers)

Outliers should be deleted from the data frame.

### Merge outliers with main df
df.article  <- merge(x= df.article  , y= outliers, by.x = "step", by.y="step", all.x = TRUE)

### Delete outliers
df.article  <-  df.article  %>% mutate(is_outlier = ifelse(diff_second < min_outlier, FALSE, TRUE))

## filter outlier values
df.article  <- df.article  %>% filter(is_outlier==FALSE)

Merge User Metadata

We will merge our last data frame with run_3.enrolments data. By doing this, we will be able to interpret on users while performing exploratory analysis.

df.article  <- merge(x=df.article , y=enrolments, by=c("learner_id"="learner_id", "run_id"="run_id"), all.x = TRUE)

Check missing values

unique(df.article[which(is.na(df.article$age_range) == TRUE), ]$learner_id)
missing_learner_id  <- unique(df.article[which(is.na(df.article$age_range) == TRUE), ]$learner_id)

filter missing learner ids

df.article <-  df.article  %>% filter(!learner_id %in% missing_learner_id)

6. Data Understanding

Exploratory Data Analysis

Plot Reading Time in Seconds For Each Article

reading_time_diff_second_by_step.png

As you can see, each article has a different range.

Calculate Average Article Reading Time

One of the business objectives was average article read time. It should be noted that geometric mean is used to reduce the effect of outlier values. The function was implemented in lib/helpers.R file.

## Calculate geometric mean for every article
df.article.mean <- df.article %>% group_by(step) %>% summarise(mean_diff_second = helper.geo_mean(diff_second))

Plot Average Article Reading Time

article_average_reading_time.png

According to bar graph, the step with the highest average is step 2.18. The name of this step is “Bitcoin: a cryptocurrency . This topic can be considered interesting. Looking at the lowest averages are 1.18 and 3.20 which names are ”Recapping our exploration of privacy” and “Wrapping up”. The common feature of these 2 steps is that it is a summary article. From this graph it can be interpreted that users are not very interested in summarizing articles.

Select an Article and Plot

Since the length of the articles is not known, each article has its own distribution in terms of reading time. Therefore, making analysis on all data will create misleading results. 2 articles will be chosen at random and continue over them.

df.article.filtered <- df.article %>% filter(step  %in% c("1.3","2.16"))

BoxPlot Elapsep Time by age

article_13_216_elapsed_time.png

According to the graph, it seems that there are too many outliers in people of unknown age. It seems that people over the age of 65 give importance to the subject of “Is your mobile phone spying on you” .

Age Distribution

article_13_216_elapsed_time_age_dist.png

As you can see in the bar graph, there is a lot of data of unknown age.

Plot Elapsed Reading Time by employment status

article_13_216_elapsed_time_employment_status.png

It seems that students give less importance to “Is your mobile phone spying on you” than other employment statuses. The median value of this is very low compared to others

Plot Elapsed Reading Time by Employment Area

article_13_216_elapsed_time_employment_area.png

As can be seen from the graphics, the time spent on the article varies according to the demographic information of the people and the subject of the article.

7. Data Preparation

Random state ensures that the splits that you generate are reproducible.

set.seed(123) # random state

Feature Selection

According to the features extracted from the analysis, a total of 7 independent variables will be used in the first model.

df_model <- df.article %>% select(step,elapsed_time,gender,age_range, highest_education_level,employment_status, employment_area)

Filter Unknown

During Exploratory Data Analysis, it was determined that the data with the value “Unknown” had too many outliers. These values should be filtered.

df_model <- df_model %>% filter(age_range != "Unknown") %>% filter(highest_education_level != "Unknown") %>% filter(employment_status != "Unknown") %>% filter(gender != "Unknown") %>% filter(employment_area  != "Unknown")

Reset Factor Levels

The type of data we filtered was factor. Therefore, you need to reset our factor level.

df_model$step <- as.factor(as.character(df_model$step))
df_model$gender <- as.factor(as.character(df_model$gender))
df_model$age_range <- as.factor(as.character(df_model$age_range))
df_model$highest_education_level <- as.factor(as.character(df_model$highest_education_level))
df_model$employment_status <- as.factor(as.character(df_model$employment_status))
df_model$employment_area <- as.factor(as.character(df_model$employment_area))

Train-Test Split

To measure the success of our model, we need to separate our data into training and testing.We will split data into 80 percent for training, 20 percent for validation.


train.index <- createDataPartition(df_model$elapsed_time, p = .8, list = FALSE)
train <- df_model[ train.index,]
test  <- df_model[-train.index,]

8. Modeling

Linear Regression Model

Fit Model

model_lm = lm(elapsed_time~., data = train) #Create the linear regression

Predict Validation Data

pred_lm <- predict(model_lm, newdata=test)

RMSE Score

RMSE(pred_lm, test$elapsed_time)
test_lm <- test
test_lm$predicted <- pred_lm 

linear_model_predict.png

line plot between actual and predicted

linear_model_100_predict.png

9. Evaluation

It can be very useful for readers to know how much time they can expect to spend on an article before they engage. It is calculated based on the number of average adult reads words in one minute. Our approach goal is to calculate it using data.Considering the findings of the project, the reading times vary according to the demographic information of the person,and also the variables that affect the reading time change for each article.

10. Deployment

This section aims to put the whole work into practice. It is not possible for the study to reach its business goal if all analyzes remain local environment. Therefore, the study needs to be integrated in the target systems. In our study, we will deploy our report to Google Firebase.

LS0tCnRpdGxlOiAiRGF0YSBNYW5hZ2VtZW50IGFuZCBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIC0gQ1NDODYzMSAtIFByb2plY3QiCmF1dGhvcjogIk11emFmZmVyIFNlbmthbCAtIDIxMDM1MTQ5MSIKZGF0ZTogIjMvMTIvMjAyMSIKb3V0cHV0OgogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyPSBub3JtYWxpemVQYXRoKCcuLicpKQpgYGAKCgpgYGB7ciwgZWNobz1GQUxTRSx3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UsZXJyb3I9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQpsaWJyYXJ5KFByb2plY3RUZW1wbGF0ZSkKbG9hZC5wcm9qZWN0KCkKCmBgYAoKIyBJbnRyb2R1Y3Rpb24KCkZ1dHVyZUxlYXJuIGlzIGFuIG9ubGluZSBvcGVuIGNvdXJzZSBwbGF0Zm9ybSBpbiB2YXJpb3VzIGZpZWxkcyBhbmQgc3ViamVjdHMuIFRoZSBjeWJlciBzZWN1cml0eSBjb3Vyc2UgaXMgcHJlc2VudGVkIGJ5IHJlc2VhcmNoZXJzIGZyb20gTmV3Y2FzdGxlIFVuaXZlcnNpdHnigJlzIFNjaG9vbCBvZiBDb21wdXRpbmcgU2NpZW5jZS4gSXQgaXMgbm90IGp1c3QgYSB2aWRlby1iYXNlZCBjb3Vyc2UsIGl0IGhhcyBzZWN0aW9ucyBzdWNoIGFzIGFydGljbGVzLCBkaXNjdXNzaW9ucyBhbmQgcXVpenplcy4gVGhpcyBjb3Vyc2UgaXMgb3BlbmVkIGluIGNlcnRhaW4gcGVyaW9kcyBhbmQgbWFueSBwZW9wbGUgYmVuZWZpdCBmcm9tIHRoaXMgY291cnNlLgoKCgojICBJbXBsZW1lbnRhdGlvbiBvZiB0aGUgQ1JJU1AtRE0KCiMgMS4gQnVzaW5lc3MgVW5kZXJzdGFuZGluZwoKQnVzaW5lc3NlcyBhcmUgbWFraW5nIGVmZm9ydCB0byBhY2hpZXZlIGFuIGVkZ2Ugd2l0aCBpdHMgY29udGVudCBtYXJrZXRpbmcgcHJvZ3JhbS4gU2hvd2luZyBhbiBhcnRpY2xl4oCZcyByZWFkaW5nIHRpbWUgdG8gZWFjaCBvZiBhcnRpY2xlIGNhbiBoYXZlIGEgcHJvZm91bmQgYW5kIHBvc2l0aXZlIGVmZmVjdCBvbiByZWFkZXIgZW5nYWdlbWVudCBsZXZlbHMuIFRoZSBmaXJzdCBiZW5lZml0IGlzIHRoYXQgdXNlcnMgdG8gaGVscCB0aGVtIGNob29zZSB0aGUgcmlnaHQgYXJ0aWNsZSBmb3IgdGhlIHJpZ2h0IGFtb3VudCBvZiBhdmFpbGFibGUgdGltZSB0aGV5IGhhdmUuIEFub3RoZXIgYWR2YW50YWdlIGlzIHRoYXQgZXN0aW1hdGVkIHJlYWRpbmcgdGltZSBpbXBhY3RzIGVuZ2FnZW1lbnQgbWV0cmljcyBzZXJpb3VzbHkuIExhc3RseSwgaXQgaGVscHMgdG8gbWFrZSBhbmFseXplcyBhY2NvcmRpbmcgdG8gY2VydGFpbiBncm91cHMuCgpSZWFkaW5nIHRpbWUgaXMgY2FsY3VsYXRlZCBiYXNlZCBvbiB0aGUgbnVtYmVyIG9mIHdvcmRzIHJlYWQgYnkgdGhlIGF2ZXJhZ2UgaHVtYW4gcGVyIG1pbnV0ZS4gR2VuZXJhbGx5LCB0aGlzIGF2ZXJhZ2UgdmFsdWUgcGVyIG1pbnV0ZSBpcyBiZXR3ZWVuIDIwMCBhbmQgMjUwIHdvcmRzLiBUaGVyZWZvcmUsIHRoZSB0b3RhbCBudW1iZXIgb2Ygd29yZHMgaW4gdGhlIGFydGljbGUgaXMgZGl2aWRlZCBieSB0aGlzIGRldGVybWluZWQgYXZlcmFnZS4KCkhvd2V2ZXIsIHNjaWVudGlmaWMgYXJ0aWNsZXMgYXJlIG5vdCBsaWtlIGJsb2cgcG9zdHMuIFJlYWRpbmcgdGltZSB2YXJpZXMgYWNjb3JkaW5nIHRvIG9uZSdzIGtub3dsZWRnZSwgYWdlLCBjb3VudHJ5IGV2ZW4gYXJ0aWNsZSBzdWJqZWN0IC5Gb3IgZXhhbXBsZSwgcHJvZmVzc2lvbmFscyBjYW4gcmVhZCBhcnRpY2xlcyBtb3JlIHF1aWNrbHkgdGhhbiBzdHVkZW50cyBvciBvbGRlciBwZW9wbGUgcmVhZCBzbG93ZXIuIEhlbmNlLCAgd2UgYXJlIG5vdCBtYWtpbmcgYW4gYWNjdXJhdGUgZXN0aW1hdGUgYnkgdXNpbmcgdGhlIGNvbnZlbnRpb25hbCBjYWxjdWxhdGlvbiBtZXRob2QuCgpJbiB0aGlzIHN0dWR5LCB0aGUgZGF0YSB3aWxsIGJlIGFuYWx5emVkIGFuZCB0aGUgYXJ0aWNsZSByZWFkaW5nIHRpbWUgd2lsbCBiZSBleGFtaW5lZCBpbiB3aGljaCBncm91cHMgYW5kIGhvdyBpdCBjaGFuZ2VzLiBUaGVuLCBhIHJlZ3Jlc3Npb24gbW9kZWwgdXNpbmcgdGhlc2UgZmVhdHVyZXMgd2lsbCBiZSBkZXZlbG9wZWQgdGhhdCBjYW4gcHJlZGljdCBhcnRpY2xlIHJlYWRpbmcgdGltZSBvbiBhIHBlci1wZXJzb24gYmFzaXMuCgoKIyMgIEJ1c2luZXNzIG9iamVjdGl2ZXMKClRoZSBmb2xsb3dpbmcgc2VjdGlvbnMgd2lsbCBmb2N1cyBvbjoKCi0gMS4gRG9lcyB0aGUgdGltZSBzcGVudCBvbiB0aGVzZSBhcnRpY2xlcyBkaWZmZXIgYWNjb3JkaW5nIHRvIGNlcnRhaW4gZ3JvdXBzID8KLSAyLiBIb3cgZWZmZWN0aXZlIGFyZSB0aGUgYXJ0aWNsZXMgaW4gdGhlIGNvdXJzZSA/IAotIDMuIFdoYXQgaXMgdGhlIGF2ZXJhZ2UgcmVhZCB0aW1lIGZvciBlYWNoIGFydGljbGUgPwotIDQuIENhbiB3ZSBlc3RpbWF0ZSB0aGUgcmVhZGluZyB0aW1lIHBlciBwZXJzb24gPwoKIyMgIERhdGEgTWluaW5nIEdvYWxzCgpUaGUgYnVzaW5lc3Mgb2JqZWN0aXZlcyBpcyBkZWZpbmVkLCBpdCdzIHRpbWUgdG8gdHJhbnNsYXRlIGl0IGludG8gYSBkYXRhIG1pbmluZyByZWFsaXR5CgotIEV4cGxvcmUgYW5kIGZpbmQgZmVhdHVyZXMgb25lIG9mIHJ1biBkYXRhIHNldHMgCi0gQnVpbGQgcHJlZGljdGl2ZSBtb2RlbCB3aXRoIDIgbWluIFJNU0UKCgoKIyAyLiBEYXRhIFVuZGVyc3RhbmRpbmcKCldlIHdpbGwgZ2F0aGVyLCBkZXNjcmliZSBhbmQgZXhwbG9yZSB0aGUgZGF0YSB0byBtYWtlIHN1cmUgaXQgZml0cyB0aGUgYnVzaW5lc3MgZ29hbC4gVGhlcmUgYXJlIDYgZGlmZmVyZW50IGNvdXJzZSBwZXJpb2RzKHJ1bnMpIGFuZCB0aGUgZGF0YSBpcyBzdG9yZWQgaW4gZGlmZmVyZW50IGZpbGVzIGFuZCB0aGVyZSBhcmUgNTMgaW4gdG90YWwuIEl0IGNhbiBjYXVzZSBjb25mdXNpb24gYW5kIGluZWZmaWNpZW5jeSBkdXJpbmcgYW5hbHlzaXMuIFdlIG11c3Qgc3RvcmUgYW5kIHVzZSB0aGVtIGluIGEgc3RydWN0dXJlZCB3YXkgaW4gb3VyIGFuYWx5c2lzLiBGb3IgdGhpcywgd2UgdHJhbnNmb3JtZWQgb3VyIGZpbGVzIGluIGEgc3RydWN0dXJlZCB3YXkgdXNpbmcgKipyZWdleCoqLiBUaGUgZnVuY3Rpb24gaXMgaW1wbGVtZW50ZWQgaW4gKipsaWJzL2dsb2JhbHMuUioqIGZpbGUuCgpFdmVyeSBydW4gaW5jbHVkZXMgOCBkYXRhIHNldHMgYXMgdGhlIGZvbGxvd2luZ3M6CgotICAgKiplbnJvbG1lbnRzOioqIGluY2x1ZGVzIHVzZXIgZGF0YQotICAgKip2aWRlby5zdGF0czoqKiBjb250YWlucyB2aWRlbyBzdGF0aXN0aWNzIGluZm9ybWF0aW9uCi0gICAqKnRlYW0ubWVtYmVyczoqKiB0ZWFtIG1lbWJlciBsaXN0IHN1Y2ggYXMgZWR1Y2F0b3IsIGhvc3QKLSAgICoqc3RlcC5hY3Rpdml0eToqKiBhbGwgdXNlcnMgZmlyc3QgdmlzaXQgYW5kIGVuZCB0aW1lIGluZm9ybWF0aW9uIGZvciBlYWNoIHN0ZXAKLSAgICoqbGVhdmluZy5zdXJ2ZXkucmVzcG9uc2VzOioqIGluY2x1ZGVzIHN1cnZleSBhbnN3ZXJzIGFmdGVyIGxlYXZpbmcgdGhlIGNvdXJzZQotICAgKip3ZWVrbHkuc2VudGltZW50LnN1cnZleS5yZXNwb25zZXM6KiogaW5jbHVkZXMgd2Vla2x5IHN1cnZleSByZXNwb25zZXMuCi0gICAqKnF1ZXN0aW9uLnJlc3BvbnNlOioqIGNvbnRhaW5zIHRoZSBhbnN3ZXJzIGdpdmVuIGJ5IHRoZSB1c2VycyBpbiB0aGUgcXVpenplcwotICAgKiphcmNoZXR5cGUuc3VydmV5LnJlc3BvbnNlczoqKiBjb250YWlucyB0aGUgYW5zd2VycyBnaXZlbiBieSB0aGUgdXNlcnMgaW4gYXJjaGV0eXBlIHN1cnZleQoKSG93ZXZlciwgcnVuIDEgYW5kIDIgZG9lcyBub3QgaW5jbHVkZSB2aWRlby5zdGF0cyBhbmQgYWxzbyBydW4gMSBkb2VzIG5vdCBpbmNsdWRlIHRlYW0ubWVtYmVycyBkYXRhLgoKIyMgR2F0aGVyaW5nIGFuZCBEZXNjcmliaW5nIERhdGEKClRvIGFjaGlldmUgb3VyIGJ1c2luZXNzIGdvYWwgLCB3ZSB3aWxsIHVzZSBzdGVwLmFjdGl2aXR5IGFuZCBlbnJvbG1lbnRzIGRhdGFzZXRzLgoKIyMjIFN0ZXAgQWN0aXZpdHkgU3VtbWFyeQoKClRoZSBkZXNjcmlwdGlvbiBvZiBlYWNoIGNvbHVtbi92YXJpYWJsZSBjYW4gYmUgc2VlbiBiZWxvdzoKCi0gICAqKmxlYXJuZXJfaWQ6KiogbGVhcm5lciB1bmlxdWUgaWQKLSAgICoqc3RlcDoqKiBzdGVwIGlkCi0gICAqKndlZWtfbnVtYmVyOioqIHdlZWsgbnVtYmVyCi0gICAqKnN0ZXBfbnVtYmVyOioqIHN0ZXAgbG93IGlkCi0gICAqKmZpcnN0X3Zpc2l0ZWRfYXQ6KiogZmlyc3QgdmlzaXRlZCB0aW1lIHdpdGggdGltZXpvbmUKLSAgICoqbGFzdF9jb21wbGV0ZWRfYXQ6KiogY29tcGxldGVkIHRpbWUgd2l0aCB0aW1lem9uZQoKCiMjIyBFbnJvbG1lbnQgU3VtbWFyeQoKLSAgICoqbGVhcm5lcl9pZDoqKiBsZWFybmVyIHVuaXF1ZSBpZAotICAgKiplbnJvbGxlZF9hdDoqKiBlbnJvbG1lbnQgZGF0ZXRpbWUKLSAgICoqdW5lbnJvbGxlZF9hdDoqKiB1bmVyb2xtZW50IGRhdGV0aW1lCi0gICAqKnJvbGU6Kiogcm9sZQotICAgKipmdWxseV9wYXJ0aWNpcGF0ZWRfYXQ6KioKLSAgICoqcHVyY2hhc2VkX3N0YXRlbWVudF9hdDoqKgotICAgKipnZW5kZXI6KioKLSAgICoqY291bnRyeToqKgotICAgKipkZXRlY3RlZF9jb3VudHJ5OioqCi0gICAqKmFnZV9yYW5nZToqKgotICAgKipoaWdoZXN0X2VkdWNhdGlvbl9sZXZlbDoqKgotICAgKiplbXBsb3ltZW50X3N0YXR1czoqKgotICAgKiplbXBsb3ltZW50X2FyZWE6KioKCk90aGVyIGRhdGEgYXJlIGFzIGZvbGxvd3M7CgohW2NsYXNzX2RpYWdyYW1dKC4uL2dyYXBocy9jbGFzc19kaWFncmFtLnBuZykKCiMjIERhdGEgUXVhbGl0eQoKQmVmb3JlIHN0YXJ0IHRoZSBhbmFseXNpcyBwYXJ0LCB3ZSBzaG91bGQgY2hlY2sgZGF0YSBxdWFsaXR5LiBGaXJzdCBvZiBhbGwsIHN0ZXAgY29sdW1uIGluIHN0ZXAuYWN0aXZpdHkgaXMgbnVtZXJpYy4gV2Ugc2hvdWxkIGNvbnZlcnQgaXQgdG8gc3RyaW5nIGNvbHVtbi4gQ29sdW1uIHRyYW5zZm9ybSBvcGVyYXRpb25zIHdlcmUgcGVyZm9ybWVkIGluIHRoZSBgbXVuZ2UvMDEtVHJhbnNmb3JtLURhdGF0eXBlLlJgIGZpbGUuCgpTaW5jZSB0aGUgc3RlcCBjb2x1bW4gaXMgbnVtZXJpYywgdGhlIHZhbHVlcyAyLjIgdG8gMi4yMCBhcHBlYXIgYXMgMi4yLiBXZSBzaG91bGQgZml4IHRoaXMgZmlyc3QsIHRoZW4gY29udmVydCBjYXRlZ29yaWNhbCBjb2x1bW4uIFRoaXMgb3BlcmF0aW9uIHdhcyBpbXBsZW1lbnRlZCBpbiBgbXVuZ2UvMDIuRml4LVN0ZXBJRC1TdGVwQWN0aXZpdHkuUmAKCkluIGVucm9sbG1lbnQgZGF0YSBzZXQsIHRoZXJlIGFyZSBtYW55IG1pc3NpbmcgdmFsdWVzIGluIGFnZV9yYW5nZSwgY291bnRyeSwgZW1wbG95bWVudF9hcmVhLiBUaGVyZSBhcmUgMiBjb2x1bW5zIGZvciBjb3VudHJ5IGluZm9ybWF0aW9uLiBPbmUgaXMgdGhlIGNvdW50cnkgbmFtZSBnaXZlbiBieSB0aGUgdXNlciwgYW5kIHRoZSBvdGhlciBpcyB0aGUgY291bnRyeSBuYW1lIGRldGVybWluZWQgYnkgdGhlIHN5c3RlbS4KCkJlZm9yZSBwcm9jZWVkaW5nIHRvIHRoZSBleHBsb3JhdG9yeSBhbmFseXNpcywgc29tZSBmZWF0dXJlIGVuZ2luZWVyaW5nIHN0ZXBzIHdlcmUgYXBwbGllZCB0byBwcm92aWRlIGEgbW9yZSBjb21mb3J0YWJsZSB2aXN1YWxpemF0aW9uIG9uIHRoZSBkYXRhLgoKTmV3IGNvbHVtbnMgYXJlIGFkZGVkIGFzIGZvbGxvd2luZzoKCi0gICAqKmNvbXBsZXRlZCoqIChzdGVwX2FjdGl2aXR5KSA6IGl0IGlzIGEgYm9vbGVhbiBjb2x1bW4gdGhhdCByZXByZXNlbnRzIHdoZXRoZXIgY29tcGxldGVkIG9yIG5vdAotICAgKippc191bmVucm9sbCoqIChlbnJvbG1lbnRzKSA6IGl0IGlzIGEgYm9vbGVhbiBjb2x1bW4gdGhhdCByZXByZXNlbnRzIHdoZXRoZXIgdW5kbyB0aGUgZW5yb2xsbWVudCBvciBub3QKClRoaXMgb3BlcmF0aW9ucyBpbXBsZW1lbnRlZCBpbiBgbXVuZ2UvMDMtRmVhdHVyZUVuZ2luZWVyaW5nLlJgCgpBbmQgYWxzbywgdG8gYW5hbHl6ZSBmb3IgYWxsIHN0ZXAgYWN0aXZpdHksIGFsbCBmaWxlcyBzaG91bGQgYmUgbWVyZ2VkLiBTbyB3ZSBuZWVkIHRvIGFkZCBhIG5ldyBjb2x1bW4gY2FsbGVkICoqcnVuX2lkKiogdG8gYWxsIG91ciBkYXRhIGFuZCBtZXJnZSB0aGUgZGF0YS4gVGhpcyBvcGVyYXRpb24gd2FzIGltcGxlbWVudGVkIGluIGBtdW5nZS8wNC1NZXJnZURhdGFGcmFtZXMuUmAKCiMjIERhdGEgRXhwbG9yYXRvcnkgQW5heWxzaXMKCgohW3J1bl8xX3N0ZXBfYWN0aXZpdHlfY291bnQucG5nXSguLi9ncmFwaHMvcnVuXzFfc3RlcF9hY3Rpdml0eV9jb3VudC5wbmcpCgoKIVtydW5fMV9zdGVwX2FjdGl2aXR5LnBuZ10oLi4vZ3JhcGhzL3J1bl8xX3N0ZXBfYWN0aXZpdHkucG5nKQoKIVtydW5fM19zdGVwX2FjdGl2aXR5LnBuZ10oLi4vZ3JhcGhzL3J1bl8zX3N0ZXBfYWN0aXZpdHkucG5nKQoKCkZpcnN0bHksIGl0IHNob3VsZCBiZSBub3RlZCBpbiB0aGUgZ3JhcGggdGhhdCB0aGUgc3RlcCBzdGVwcyBhcmUgbm90IHNlcXVlbnRpYWwuIFRoZSBtb3N0IHN0cmlraW5nIHBhcnQgb2YgdGhlIGdyYXBoIGlzIHRoYXQgYXBwcm94aW1hdGVseSA3NTAgc3R1ZGVudHMgZGlkIG5vdCBjb21wbGV0ZSAqKnN0ZXAgMS4xKiouIFRoZSBudW1iZXIgb2YgY29tcGxldGluZyB0aGUgc3RlcCBzdGFydHMgd2l0aCAxNTAwLiBBZnRlciAxMCBzdGVwcywgdGhpcyBudW1iZXIgdmFyaWVzIGJldHdlZW4gNTAwIGFuZCA3NTAgZm9yIHRoZSByZXN0IG9mIHRoZSBjb3Vyc2UuIEluIGFkZGl0aW9uLCB0aGUgbnVtYmVyIG9mIHBhcnRpY2lwYXRpb24gaW4gKipzdGVwIDMuMTgqKiBpcyBxdWl0ZSBsb3cuCgohW2FsbF9zdGVwX2FjdGl2aXR5LnBuZ10oLi4vZ3JhcGhzL2FsbF9zdGVwX2FjdGl2aXR5LnBuZykKCldoZW4gd2UgbG9vayBhdCB0aGUgZ3JhcGhpY3MgZm9yIGVhY2ggcnVuLCB3ZSBzZWUgdGhhdCB0aGVyZSBpcyBhIHNpbWlsYXIgcGF0dGVybi4gSG93ZXZlciwgSW4gdGhlIGxhc3Qgc3RlcHMgb2YgUnVuIDEsIGl0IHNlZW1zIHRoYXQgdGhlIHJhdGUgb2Ygbm9uLWNvbXBsZXRpb24gaXMgaGlnaGVyIHRoYW4gdGhlIG90aGVycyBhbmQgdGhlIG92ZXJhbGwgbnVtYmVyIG9mIGVucm9sbG1lbnQgaXMgYWxzbyBxdWl0ZSBoaWdoLgoKV2hlbiBsb29rZWQgY2FyZWZ1bGx5LCB0aGVyZSBhcmUgMiBzcGFjZXMgaW4gUnVuIDMuIFdoZXJlYXMsIGEgZ2FwIHdhcyBvY2N1cmVkIGluIG91ciBmaXJzdCBjaGFydCBvbmx5IGF0ICoqc3RlcCAzLjE4KiouIEFuZCBhbHNvIHRoZXNlIGdhcHMgd2VyZSBmb3JtZWQgaW4gdGhlIHJ1bmVzIG90aGVyIHRoYW4gUnVuIDEuIEl0IHJhaXNlcyBzdXNwaWNpb24gdGhhdCB0aGVyZSBtYXkgYmUgYW55IG1pc3Npbmcgc3RlcC4gTGV0J3MgY2hlY2sgaXQuCgpgYGB7cn0Kc3RlcF9hY3Rpdml0eSAlPiUKICBncm91cF9ieShydW5faWQpICU+JQogIHN1bW1hcmlzZShuX2Rpc3RpbmN0KHN0ZXApKQpgYGAKCkFzIHdlIHRob3VnaHQsIHRoZXJlIGFyZSBtaXNzaW5nIGFuZCBleHRyYSBzdGVwcy4gQmVjYXVzZSBvZiB0aGVzZSBkaWZmZXJlbnQgc3RlcHMsIHdlIGNhbiBtaXNpbnRlcnByZXQgb3VyIGRhdGEuCgoKIyAzLiBEYXRhIFByZXBhcmF0aW9uCgojIyBGZWF0dXJlIEVuZ2luZWVyaW5nCgojIyAgIEV4dGVybmFsIERhdGEgLSBDb250aW5lbnRzCgpDb3VudHJ5IGJhc2VkIHZpc3VhbGl6YXRpb25zIGNhbiBhbHNvIGJlIGRpZmZpY3VsdCB0byBpbnRlcnByZXQgYXMgdGhlcmUgYXJlIG1hbnkgY291bnRyeSBuYW1lcy4gRm9yIHRoaXMsIGNvbnRpbmVudCBkYXRhIHRoYXQgbWFwcGVkIGNvdW50cmllcyBmcm9tIG91dHNpZGUgd2FzIGZvdW5kIGFuZCBtZXJnZWQgd2l0aCBlbnJvbGxtZW50IGRhdGEuCgoKCiMjICBFeHRlcm5hbCBEYXRhIC0gU3RlcCBBY3Rpdml0eSBNZXRhZGF0YSAKV2Ugd2lsbCBjcmVhdGUgYSBtYXAgbGlzdCB3aGljaCBpbmNsdWRlcyBuYW1lcywgdHlwZXMgYW5kIHN0ZXBfaWQuIAoKCgpgYGB7cn0KaGVhZChydW5fMy5zdGVwLm1ldGFkYXRhKQpgYGAKVGhlbiwgd2Ugd2lsbCBtZXJnZSB3aXRoIHN0ZXAgYWN0aXZpdHkgZGF0YSBmcmFtZXMuCgpgYGB7cn0KZGYuZmlsdGVyZWQgPC0gc3RlcF9hY3Rpdml0eSAlPiUgZHBseXI6OmZpbHRlcighcnVuX2lkICVpbiUgYygxLDIpKSAjIHJ1biAxICBhbmQgcnVuIDIgYXJlIG5vdCBzYW1lIHN0cnVjdHVyZQpkZi5maWx0ZXJlZCA8LSAgbWVyZ2UoeD0gZGYuZmlsdGVyZWQsIHkgPSBydW5fMy5zdGVwLm1ldGFkYXRhLCBieS54ID0gInN0ZXAiLCBieS55PSJzdGVwX2lkIiwgYWxsLnggPSBUUlVFKQoKYGBgCgoKIyMgIENhbGN1bGF0aW5nIFRpbWUgRGlmZmVyZW5jZQoKCkluIG9yZGVyIHRvIGFjaGlldmUgb3VyIGJ1c2luZXNzIGdvYWwsIHdlIG5lZWQgdG8gY2FsY3VsYXRlIHRoZSB0b3RhbCBlbGFwc2VkIHRpbWUuIFdlIGNhbiBmaW5kIGRpZmZlcmVuY2UgdGltZSBiZXR3ZWVuICoqZmlyc3RfdmlzaXRlZF9hdCoqIGFuZCAqKmxhc3RfY29tcGxldGVkX2F0KiouU2luY2Ugc29tZSByZWNvcmRzIGRvIG5vdCBoYXZlIGEgbGFzdCB0aW1lIGRhdGUsIHdlIHdpbGwgc3BlY2lmeSB0aGVpciB2YWx1ZSBhcyAwLiAKCgpJdCB3YXMgc3RhdGVkIGluIG9uZSBvZiB0aGUgYnVzaW5lc3Mgb2JqZWN0aXZlIHRoYXQgdGhlIHJlYWRpbmcgZGF0ZSB3b3VsZCBiZSBlc3RpbWF0ZWQuIApJdCB3aWxsIGJlIGJldHRlciBpbiB0ZXJtcyBvZiB1c2VyIGV4cGVyaWVuY2UgdG8gZXN0aW1hdGUgdGhpcyBlc3RpbWF0ZSBpbiBoYWxmLW1pbnV0ZSBpbnRlcnZhbHMgaW5zdGVhZCBvZiBzZWNvbmRzLiBXZSBuZWVkIHRvIHJvdW5kIHRoZSBkYXRhIGluIHNlY29uZHMgdG8gbnVtYmVycyBzdWNoIGFzIDYuMzAgbWludXRlcywgNyBtaW51dGVzLiBUaGUgbmFtZSBvZiB0aGlzIG5ld2x5IGNyZWF0ZWQgY29sdW1uIHdpbGwgYmUgY2FsbGVkICoqZWxhcHNlZF90aW1lKiouCgpUaGVzZSBvcGVyYXRpb25zIHdlcmUgaW1wbGVtZW50ZWQgaW4gYG11bmdlLzAzLUZlYXR1cmVFbmdpbmVlcmluZy5SYAoKCiMjIEZpbHRlciBEYXRhIEZyYW1lCgpXZSBvbmx5IG5lZWQgdG8gZmlsdGVyIHJlY29yZHMgdGhhdCBoYXZlIGEgbGFzdCBjb21wbGV0ZWQgZGF0ZS4KCmBgYHtyfQoKZGYuZmlsdGVyZWQgIDwtIGRmLmZpbHRlcmVkICAlPiUgZmlsdGVyKGRpZmZfc2Vjb25kICE9IDApCgpgYGAKCgoKCiMgNC4gRGF0YSBVbmRlcnN0YW5kaW5nCgpBcyB3ZSBrbm93LCAqKkNSSVNQLURNKiogaXMgbm90IGEgbGluZWFyIHByb2Nlc3MuIFRvIG1ha2Ugc3VyZSBpdCBmaXRzIHRoZSBidXNpbmVzcyBhbmQgZGF0YSBtaW5pbmcgZ29hbCwgd2UgY2FuIGdvIGJhY2sgdG8gZGF0YSB1bmRlcnN0YW5kaW5nLgoKVGhlcmUgYXJlIGh0bWwgZmlsZXMgYXR0YWNoZWQgd2l0aCBkZXRhaWxzIGFib3V0IHRoZSBjb3Vyc2UgaW4gZGF0YSBmaWxlcy4gVGhlc2UgaHRtbCBmaWxlcyBjb250YWluIHRoZSBuYW1lcyBhbmQgc3RlcCB0eXBlcyBvZiBhbGwgdGhlIHN0ZXBzLiBXZSBjYW4gdXNlIHRoZW0gdG8gZmluZCBtaXNzaW5nIGFuZCBleHRyYSBzdGVwcy4gQW5kIGFsc28sIHdlIG5lZWQgc3RlcCB0eXBlcyhhcnRpY2xlLCB2aWRlbywgZXRjKSBpbiBvdXIgYW5hbHlzaXMuCgoKIyMgIERhdGEgRXhwbG9yYXRvcnkgQW5heWxzaXMKCgohW3J1bjNfc3RlcF9hY3Rpdml0eV93aXRoX3R5cGUucG5nXSguLi9ncmFwaHMvcnVuM19zdGVwX2FjdGl2aXR5X3dpdGhfdHlwZS5wbmcpCgoKVGhpcyBiYXIgY2hhcnQgcmVwcmVzZW50cyB0aGUgbnVtYmVyIG9mIHBhcnRpY2lwYXRpb24gb2YgZWFjaCBzdGVwIHdpdGggdGhlaXIgc3RlcCB0eXBlLiAKCgojIyBCb3hwbG90IG9mIEVsYXBzZWQgdGltZSBmb3IgZWFjaCBzdGVwCgoKIVtmaWx0ZXJlZF9ydW5fZWxhcHNlZF90aW1lX2JveHBsb3RfYnlfc3RlcC5wbmddKC4uL2dyYXBocy9maWx0ZXJlZF9ydW5fZWxhcHNlZF90aW1lX2JveHBsb3RfYnlfc3RlcC5wbmcpCgpGaXJzdCBvZiBhbGwsIHNpbmNlIHRoZXJlIGFyZSB0b28gbWFueSBzdGVwcywgaXQgaXMgZGlmZmljdWx0IHRvIGRldGVybWluZSB3aGljaCBzdGVwIHRoZSBwb2ludHMgYmVsb25nIHRvLiBIb3dldmVyLCBpdCBpcyBjbGVhciB0aGF0IHRoZXJlIGFyZSBtYW55IG91dGxpZXJzIGluIHRoZSBkYXRhLiBJdCBtaWdodCBiZSBiZXR0ZXIgdG8gc2VlIHRoZW0gb24gdGhlIGJveCBwbG90LgoKCiMjIyBCb3hwbG90IG9mIGVsYXBzZWQgdGltZSBmb3IgZWFjaCB0eXBlCgohW2ZpbHRlcmVkX3J1bl9lbGFwc2VkX3RpbWVfYm94cGxvdC5wbmddKC4uL2dyYXBocy9maWx0ZXJlZF9ydW5fZWxhcHNlZF90aW1lX2JveHBsb3QucG5nKQoKClNvbWUgdXNlcnMgbWF5IGhhdmUgbGVmdCB0aGUgc2l0ZSBvcGVuIGR1cmluZyBzb21lIHN0ZXBzIGFuZCBsZWZ0IHRoZSBjb21wdXRlci4gU28gd2UgbWF5IGJlIHNlZWluZyB0aGVzZSBvdXRsaWVycy4gSXQgc2VlbXMgZGlmZmljdWx0IHRvIGludGVycHJldCB0aGUgZ3JhcGhzIGJlY2F1c2UgdGhlcmUgYXJlIG91dGxpZXJzLCBzbyB3ZSBzaG91bGQgY2xlYW4gdGhlc2Ugb3V0bGllcnMuIFRoZSBpbnRlcnF1YXJ0aWxlIHJhbmdlIGlzIGNvbW1vbiBtZXRob2QgdXNlZCB0byBmaW5kIG91dGxpZXJzIGluIGRhdGEuIEJhc2ljYWxseSwgd2UgY2FuIGltcGxlbWVudCB0aGlzIG1ldGhvZCB0byBjbGVhbiBvdXIgb3V0bGllcnMuCgpPbiB0aGUgb3RoZXIgaGFuZCwgaW4gb3JkZXIgdG8gbWFrZSBhIGRlbW9ncmFwaGljIGludGVycHJldGF0aW9uLCB3ZSBuZWVkIHRvIGNvbWJpbmUgb3VyIGRhdGEgd2l0aCB0aGUgZW5yb2xtZW50IGRhdGEgc2V0LgoKCgojIDUuIERhdGEgUHJlcGFyYXRpb24KCiMjIEZpbHRlciBTdGVwcwoKSW4gdGhlIHJlbWFpbmluZyBhbmFseXNpcywgYW5hbHlzaXMgd2lsbCBiZSBtYWRlIG9uIHRoZSBhcnRpY2xlcy4gVGhlcmVmb3JlLCBpdCBpcyBuZWNlc3NhcnkgdG8gZmlsdGVyIHRoZSBkYXRhIGFuZCByZXRyaWV2ZSBvbmx5IHRoZSBhcnRpY2xlcy4KCmBgYHtyfQpkZi5hcnRpY2xlIDwtIGRmLmZpbHRlcmVkICAlPiUgZmlsdGVyKHN0ZXBfdHlwZSA9PSAiQVJUSUNMRSIpCnVuaXF1ZShkZi5hcnRpY2xlJHN0ZXApCmBgYAoKCiMjIyAgT3V0bGllciBEZXRlY3Rpb24KCldoZW4gdGhlIGRhdGEgaXMgYW5hbHl6ZWQsIHdlIHNlZSB0aGF0IHRoZSBvdXRsaWVycyBhcmUgcXVpdGUgbGFyZ2UuIEl0IHNlZW1zIHRoYXQgdGhleSBzcGVuZCBtb3JlIHRoYW4gMTAgaG91cnMgd2l0aCBzdGVwcywgYnV0IHN1Y2ggYSB0aGluZyBpcyBub3QgcG9zc2libGUuIFRvIGF2b2lkIHRoaXMgaXNzdWUsIGl0IHNob3VsZCBiZSBkZWZpbmVkIGEgdGhyZXNob2xkIGZvciB0aGVzZSB2YWx1ZXMgdGhlbiBmaWx0ZXJlZCBpdC4KCmBgYHtyfQp0aHJlc2hvbGQgPC0gMzAgKiA2MCAjIDMwIG1pbnV0ZQoKZGYuYXJ0aWNsZSA8LSBkZi5hcnRpY2xlICU+JSBmaWx0ZXIoZGlmZl9zZWNvbmQgPCB0aHJlc2hvbGQpCmBgYAoKCk9ubHkgdGhpcyB3YXkgb2YgZmlsdGVyaW5nIGRvZXMgbm90IHJlbW92ZSBvdXRsaWVycyBmcm9tIHRoZSBkYXRhLiBBbiBvdXRsaWVyIGRldGVjdGlvbiBtZXRob2QgbmVlZHMgdG8gYmUgYXBwbGllZC4gSVFSIG1ldGhvZCB3aWxsIGJlIHVzZWQgaW4gdGhpcyBzdHVkeS4gSW4gUHJvamVjdFRlbXBsYXRlIHN0cnVjdHVyZSwgbGliIGZvbGRlciBpcyBnb29kIHBsYWNlIHRvIHN0b3JlIGV4dGVybmFsIGZ1bmN0aW9uLiBTbywgb3VyIG91dGxpZXIgZGV0ZWN0aW9uIG1ldGhvZCB3aGljaCBJUVIgd2FzIGltcGxlbWVudGVkIGluICoqbGliL2hlbHBlcnMuUioqIGZpbGUuIAoKSW1wb3J0YW50bHksIGluc3RlYWQgb2YgY2FsY3VsYXRpbmcgYW4gb3V0bGllciBvdmVyIHRoZSBlbnRpcmUgZGF0YSBkaXJlY3RseSwgd2Ugd2lsbCBjYWxjdWxhdGUgYW4gb3V0bGllciBmb3IgZWFjaCBzdGVwLgoKYGBge3J9Cm91dGxpZXJzIDwtIGRmLmFydGljbGUgICU+JSBncm91cF9ieShzdGVwKSAlPiUgc3VtbWFyaXNlKHZhbD1oZWxwZXIuaXFyKGRpZmZfc2Vjb25kKSkgJT4lIHN1bW1hcmlzZShtaW5fb3V0bGllcj1taW4odmFsKSkKaGVhZChvdXRsaWVycykKYGBgCgpPdXRsaWVycyBzaG91bGQgYmUgZGVsZXRlZCBmcm9tIHRoZSBkYXRhIGZyYW1lLgoKYGBge3J9CiMjIyBNZXJnZSBvdXRsaWVycyB3aXRoIG1haW4gZGYKZGYuYXJ0aWNsZSAgPC0gbWVyZ2UoeD0gZGYuYXJ0aWNsZSAgLCB5PSBvdXRsaWVycywgYnkueCA9ICJzdGVwIiwgYnkueT0ic3RlcCIsIGFsbC54ID0gVFJVRSkKCiMjIyBEZWxldGUgb3V0bGllcnMKZGYuYXJ0aWNsZSAgPC0gIGRmLmFydGljbGUgICU+JSBtdXRhdGUoaXNfb3V0bGllciA9IGlmZWxzZShkaWZmX3NlY29uZCA8IG1pbl9vdXRsaWVyLCBGQUxTRSwgVFJVRSkpCgojIyBmaWx0ZXIgb3V0bGllciB2YWx1ZXMKZGYuYXJ0aWNsZSAgPC0gZGYuYXJ0aWNsZSAgJT4lIGZpbHRlcihpc19vdXRsaWVyPT1GQUxTRSkKCmBgYAoKCgoKIyMjIE1lcmdlIFVzZXIgTWV0YWRhdGEKCldlIHdpbGwgbWVyZ2Ugb3VyIGxhc3QgZGF0YSBmcmFtZSB3aXRoIHJ1bl8zLmVucm9sbWVudHMgZGF0YS4gQnkgZG9pbmcgdGhpcywgd2Ugd2lsbCBiZSBhYmxlIHRvIGludGVycHJldCBvbiB1c2VycyB3aGlsZSBwZXJmb3JtaW5nIGV4cGxvcmF0b3J5IGFuYWx5c2lzLgoKYGBge3J9CmRmLmFydGljbGUgIDwtIG1lcmdlKHg9ZGYuYXJ0aWNsZSAsIHk9ZW5yb2xtZW50cywgYnk9YygibGVhcm5lcl9pZCI9ImxlYXJuZXJfaWQiLCAicnVuX2lkIj0icnVuX2lkIiksIGFsbC54ID0gVFJVRSkKCmBgYAoKIyMjIENoZWNrIG1pc3NpbmcgdmFsdWVzCgpgYGB7cn0KdW5pcXVlKGRmLmFydGljbGVbd2hpY2goaXMubmEoZGYuYXJ0aWNsZSRhZ2VfcmFuZ2UpID09IFRSVUUpLCBdJGxlYXJuZXJfaWQpCmBgYAoKCmBgYHtyfQptaXNzaW5nX2xlYXJuZXJfaWQgIDwtIHVuaXF1ZShkZi5hcnRpY2xlW3doaWNoKGlzLm5hKGRmLmFydGljbGUkYWdlX3JhbmdlKSA9PSBUUlVFKSwgXSRsZWFybmVyX2lkKQpgYGAKCgojIyMgZmlsdGVyIG1pc3NpbmcgbGVhcm5lciBpZHMKYGBge3J9CmRmLmFydGljbGUgPC0gIGRmLmFydGljbGUgICU+JSBmaWx0ZXIoIWxlYXJuZXJfaWQgJWluJSBtaXNzaW5nX2xlYXJuZXJfaWQpCmBgYAoKCgojIDYuIERhdGEgVW5kZXJzdGFuZGluZwoKIyMgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcwoKCiMjIFBsb3QgUmVhZGluZyBUaW1lIGluIFNlY29uZHMgRm9yIEVhY2ggQXJ0aWNsZQoKCiFbcmVhZGluZ190aW1lX2RpZmZfc2Vjb25kX2J5X3N0ZXAucG5nXSguLi9ncmFwaHMvcmVhZGluZ190aW1lX2RpZmZfc2Vjb25kX2J5X3N0ZXAucG5nKQoKCkFzIHlvdSBjYW4gc2VlLCBlYWNoIGFydGljbGUgaGFzIGEgZGlmZmVyZW50IHJhbmdlLgoKIyMjICBDYWxjdWxhdGUgQXZlcmFnZSBBcnRpY2xlIFJlYWRpbmcgVGltZQoKT25lIG9mIHRoZSBidXNpbmVzcyBvYmplY3RpdmVzIHdhcyBhdmVyYWdlIGFydGljbGUgcmVhZCB0aW1lLiBJdCBzaG91bGQgYmUgbm90ZWQgdGhhdCAqKmdlb21ldHJpYyBtZWFuKiogaXMgdXNlZCB0byByZWR1Y2UgdGhlIGVmZmVjdCBvZiBvdXRsaWVyIHZhbHVlcy4KVGhlIGZ1bmN0aW9uIHdhcyBpbXBsZW1lbnRlZCBpbiBgbGliL2hlbHBlcnMuUmAgZmlsZS4KCmBgYHtyfQojIyBDYWxjdWxhdGUgZ2VvbWV0cmljIG1lYW4gZm9yIGV2ZXJ5IGFydGljbGUKZGYuYXJ0aWNsZS5tZWFuIDwtIGRmLmFydGljbGUgJT4lIGdyb3VwX2J5KHN0ZXApICU+JSBzdW1tYXJpc2UobWVhbl9kaWZmX3NlY29uZCA9IGhlbHBlci5nZW9fbWVhbihkaWZmX3NlY29uZCkpCgpgYGAKCgoKCiMjIyAgUGxvdCBBdmVyYWdlIEFydGljbGUgUmVhZGluZyBUaW1lCgohW2FydGljbGVfYXZlcmFnZV9yZWFkaW5nX3RpbWUucG5nXSguLi9ncmFwaHMvYXJ0aWNsZV9hdmVyYWdlX3JlYWRpbmdfdGltZS5wbmcpCgoKQWNjb3JkaW5nIHRvIGJhciBncmFwaCwgdGhlIHN0ZXAgd2l0aCB0aGUgaGlnaGVzdCBhdmVyYWdlIGlzICoqc3RlcCAyLjE4KiouIFRoZSBuYW1lIG9mIHRoaXMgc3RlcCBpcyAqIkJpdGNvaW46IGEgY3J5cHRvY3VycmVuY3kgKi4gVGhpcyB0b3BpYyBjYW4gYmUgY29uc2lkZXJlZCBpbnRlcmVzdGluZy4gTG9va2luZyBhdCB0aGUgbG93ZXN0IGF2ZXJhZ2VzIGFyZSAxLjE4IGFuZCAzLjIwIHdoaWNoIG5hbWVzIGFyZSAqIlJlY2FwcGluZyBvdXIgZXhwbG9yYXRpb24gb2YgcHJpdmFjeSIqIGFuZCAqIldyYXBwaW5nIHVwIiouIFRoZSBjb21tb24gZmVhdHVyZSBvZiB0aGVzZSAyIHN0ZXBzIGlzIHRoYXQgaXQgaXMgYSBzdW1tYXJ5IGFydGljbGUuIEZyb20gdGhpcyBncmFwaCBpdCBjYW4gYmUgaW50ZXJwcmV0ZWQgdGhhdCB1c2VycyBhcmUgbm90IHZlcnkgaW50ZXJlc3RlZCBpbiBzdW1tYXJpemluZyBhcnRpY2xlcy4KCgojIyMgU2VsZWN0IGFuIEFydGljbGUgYW5kIFBsb3QKClNpbmNlIHRoZSBsZW5ndGggb2YgdGhlIGFydGljbGVzIGlzIG5vdCBrbm93biwgZWFjaCBhcnRpY2xlIGhhcyBpdHMgb3duIGRpc3RyaWJ1dGlvbiBpbiB0ZXJtcyBvZiByZWFkaW5nIHRpbWUuIFRoZXJlZm9yZSwgbWFraW5nIGFuYWx5c2lzIG9uIGFsbCBkYXRhIHdpbGwgY3JlYXRlIG1pc2xlYWRpbmcgcmVzdWx0cy4gMiBhcnRpY2xlcyB3aWxsIGJlIGNob3NlbiBhdCByYW5kb20gYW5kIGNvbnRpbnVlIG92ZXIgdGhlbS4KCgoKYGBge3J9CmRmLmFydGljbGUuZmlsdGVyZWQgPC0gZGYuYXJ0aWNsZSAlPiUgZmlsdGVyKHN0ZXAgICVpbiUgYygiMS4zIiwiMi4xNiIpKQpgYGAKCgoKIyMgQm94UGxvdCBFbGFwc2VwIFRpbWUgYnkgYWdlCgohW2FydGljbGVfMTNfMjE2X2VsYXBzZWRfdGltZS5wbmddKC4uL2dyYXBocy9hcnRpY2xlXzEzXzIxNl9lbGFwc2VkX3RpbWUucG5nKQoKCkFjY29yZGluZyB0byB0aGUgZ3JhcGgsIGl0IHNlZW1zIHRoYXQgdGhlcmUgYXJlIHRvbyBtYW55IG91dGxpZXJzIGluIHBlb3BsZSBvZiB1bmtub3duIGFnZS4KSXQgc2VlbXMgdGhhdCBwZW9wbGUgb3ZlciB0aGUgYWdlIG9mIDY1IGdpdmUgaW1wb3J0YW5jZSB0byB0aGUgc3ViamVjdCBvZiAiSXMgeW91ciBtb2JpbGUgcGhvbmUgc3B5aW5nIG9uIHlvdSIgLgoKCiMjIyBBZ2UgRGlzdHJpYnV0aW9uCgohW2FydGljbGVfMTNfMjE2X2VsYXBzZWRfdGltZV9hZ2VfZGlzdC5wbmddKC4uL2dyYXBocy9hcnRpY2xlXzEzXzIxNl9lbGFwc2VkX3RpbWVfYWdlX2Rpc3QucG5nKQoKCkFzIHlvdSBjYW4gc2VlIGluIHRoZSBiYXIgZ3JhcGgsIHRoZXJlIGlzIGEgbG90IG9mIGRhdGEgb2YgdW5rbm93biBhZ2UuCgoKCiMjIFBsb3QgRWxhcHNlZCBSZWFkaW5nIFRpbWUgYnkgZW1wbG95bWVudCBzdGF0dXMKCiFbYXJ0aWNsZV8xM18yMTZfZWxhcHNlZF90aW1lX2VtcGxveW1lbnRfc3RhdHVzLnBuZ10oLi4vZ3JhcGhzL2FydGljbGVfMTNfMjE2X2VsYXBzZWRfdGltZV9lbXBsb3ltZW50X3N0YXR1cy5wbmcpCgoKCgpJdCBzZWVtcyB0aGF0IHN0dWRlbnRzIGdpdmUgbGVzcyBpbXBvcnRhbmNlIHRvICJJcyB5b3VyIG1vYmlsZSBwaG9uZSBzcHlpbmcgb24geW91IiB0aGFuIG90aGVyIGVtcGxveW1lbnQgc3RhdHVzZXMuIFRoZSBtZWRpYW4gdmFsdWUgb2YgdGhpcyBpcyB2ZXJ5IGxvdyBjb21wYXJlZCB0byBvdGhlcnMKCgojIyBQbG90IEVsYXBzZWQgUmVhZGluZyBUaW1lIGJ5IEVtcGxveW1lbnQgQXJlYQoKCiFbYXJ0aWNsZV8xM18yMTZfZWxhcHNlZF90aW1lX2VtcGxveW1lbnRfYXJlYS5wbmddKC4uL2dyYXBocy9hcnRpY2xlXzEzXzIxNl9lbGFwc2VkX3RpbWVfZW1wbG95bWVudF9hcmVhLnBuZykKCgpBcyBjYW4gYmUgc2VlbiBmcm9tIHRoZSBncmFwaGljcywgdGhlIHRpbWUgc3BlbnQgb24gdGhlIGFydGljbGUgdmFyaWVzIGFjY29yZGluZyB0byB0aGUgZGVtb2dyYXBoaWMgaW5mb3JtYXRpb24gb2YgdGhlIHBlb3BsZSBhbmQgdGhlIHN1YmplY3Qgb2YgdGhlIGFydGljbGUuCgoKCiMgNy4gRGF0YSBQcmVwYXJhdGlvbgoKUmFuZG9tIHN0YXRlIGVuc3VyZXMgdGhhdCB0aGUgc3BsaXRzIHRoYXQgeW91IGdlbmVyYXRlIGFyZSByZXByb2R1Y2libGUuCgpgYGB7cn0Kc2V0LnNlZWQoMTIzKSAjIHJhbmRvbSBzdGF0ZQpgYGAKCgojIyBGZWF0dXJlIFNlbGVjdGlvbgoKCkFjY29yZGluZyB0byB0aGUgZmVhdHVyZXMgZXh0cmFjdGVkIGZyb20gdGhlIGFuYWx5c2lzLCBhIHRvdGFsIG9mIDcgaW5kZXBlbmRlbnQgdmFyaWFibGVzIHdpbGwgYmUgdXNlZCBpbiB0aGUgZmlyc3QgbW9kZWwuCgpgYGB7cn0KZGZfbW9kZWwgPC0gZGYuYXJ0aWNsZSAlPiUgc2VsZWN0KHN0ZXAsZWxhcHNlZF90aW1lLGdlbmRlcixhZ2VfcmFuZ2UsIGhpZ2hlc3RfZWR1Y2F0aW9uX2xldmVsLGVtcGxveW1lbnRfc3RhdHVzLCBlbXBsb3ltZW50X2FyZWEpCmBgYAoKCiMjIEZpbHRlciBVbmtub3duCgpEdXJpbmcgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcywgaXQgd2FzIGRldGVybWluZWQgdGhhdCB0aGUgZGF0YSB3aXRoIHRoZSB2YWx1ZSAiVW5rbm93biIgaGFkIHRvbyBtYW55IG91dGxpZXJzLiBUaGVzZSB2YWx1ZXMgc2hvdWxkIGJlIGZpbHRlcmVkLgoKYGBge3J9CmRmX21vZGVsIDwtIGRmX21vZGVsICU+JSBmaWx0ZXIoYWdlX3JhbmdlICE9ICJVbmtub3duIikgJT4lIGZpbHRlcihoaWdoZXN0X2VkdWNhdGlvbl9sZXZlbCAhPSAiVW5rbm93biIpICU+JSBmaWx0ZXIoZW1wbG95bWVudF9zdGF0dXMgIT0gIlVua25vd24iKSAlPiUgZmlsdGVyKGdlbmRlciAhPSAiVW5rbm93biIpICU+JSBmaWx0ZXIoZW1wbG95bWVudF9hcmVhICAhPSAiVW5rbm93biIpCmBgYAoKCiMjIFJlc2V0IEZhY3RvciBMZXZlbHMKClRoZSB0eXBlIG9mIGRhdGEgd2UgZmlsdGVyZWQgd2FzIGZhY3Rvci4gVGhlcmVmb3JlLCB5b3UgbmVlZCB0byByZXNldCBvdXIgZmFjdG9yIGxldmVsLgoKYGBge3J9CmRmX21vZGVsJHN0ZXAgPC0gYXMuZmFjdG9yKGFzLmNoYXJhY3RlcihkZl9tb2RlbCRzdGVwKSkKZGZfbW9kZWwkZ2VuZGVyIDwtIGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIoZGZfbW9kZWwkZ2VuZGVyKSkKZGZfbW9kZWwkYWdlX3JhbmdlIDwtIGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIoZGZfbW9kZWwkYWdlX3JhbmdlKSkKZGZfbW9kZWwkaGlnaGVzdF9lZHVjYXRpb25fbGV2ZWwgPC0gYXMuZmFjdG9yKGFzLmNoYXJhY3RlcihkZl9tb2RlbCRoaWdoZXN0X2VkdWNhdGlvbl9sZXZlbCkpCmRmX21vZGVsJGVtcGxveW1lbnRfc3RhdHVzIDwtIGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIoZGZfbW9kZWwkZW1wbG95bWVudF9zdGF0dXMpKQpkZl9tb2RlbCRlbXBsb3ltZW50X2FyZWEgPC0gYXMuZmFjdG9yKGFzLmNoYXJhY3RlcihkZl9tb2RlbCRlbXBsb3ltZW50X2FyZWEpKQpgYGAKCiMjIyBUcmFpbi1UZXN0IFNwbGl0CgpUbyBtZWFzdXJlIHRoZSBzdWNjZXNzIG9mIG91ciBtb2RlbCwgd2UgbmVlZCB0byBzZXBhcmF0ZSBvdXIgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nLldlIHdpbGwgc3BsaXQgZGF0YSBpbnRvIDgwIHBlcmNlbnQgZm9yIHRyYWluaW5nLCAyMCBwZXJjZW50IGZvciB2YWxpZGF0aW9uLgoKCmBgYHtyfQoKdHJhaW4uaW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihkZl9tb2RlbCRlbGFwc2VkX3RpbWUsIHAgPSAuOCwgbGlzdCA9IEZBTFNFKQp0cmFpbiA8LSBkZl9tb2RlbFsgdHJhaW4uaW5kZXgsXQp0ZXN0ICA8LSBkZl9tb2RlbFstdHJhaW4uaW5kZXgsXQpgYGAKCgoKCiMgOC4gTW9kZWxpbmcKCgojIyBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbAoKIyMjIEZpdCBNb2RlbApgYGB7cn0KbW9kZWxfbG0gPSBsbShlbGFwc2VkX3RpbWV+LiwgZGF0YSA9IHRyYWluKSAjQ3JlYXRlIHRoZSBsaW5lYXIgcmVncmVzc2lvbgpgYGAKCgojIyBQcmVkaWN0IFZhbGlkYXRpb24gRGF0YQpgYGB7cn0KcHJlZF9sbSA8LSBwcmVkaWN0KG1vZGVsX2xtLCBuZXdkYXRhPXRlc3QpCmBgYAoKIyMgUk1TRSBTY29yZQoKCmBgYHtyfQpSTVNFKHByZWRfbG0sIHRlc3QkZWxhcHNlZF90aW1lKQpgYGAKCmBgYHtyfQp0ZXN0X2xtIDwtIHRlc3QKdGVzdF9sbSRwcmVkaWN0ZWQgPC0gcHJlZF9sbSAKYGBgCgohW2xpbmVhcl9tb2RlbF9wcmVkaWN0LnBuZ10oLi4vZ3JhcGhzL2xpbmVhcl9tb2RlbF9wcmVkaWN0LnBuZykKCiMjIGxpbmUgcGxvdCBiZXR3ZWVuIGFjdHVhbCAgYW5kIHByZWRpY3RlZAohW2xpbmVhcl9tb2RlbF8xMDBfcHJlZGljdC5wbmddKC4uL2dyYXBocy9saW5lYXJfbW9kZWxfMTAwX3ByZWRpY3QucG5nKQoKCgoKXG5ld3BhZ2UKCiMgOS4gRXZhbHVhdGlvbgoKSXQgY2FuIGJlIHZlcnkgdXNlZnVsIGZvciByZWFkZXJzIHRvIGtub3cgaG93IG11Y2ggdGltZSB0aGV5IGNhbiBleHBlY3QgdG8gc3BlbmQgb24gYW4gYXJ0aWNsZSBiZWZvcmUgdGhleSBlbmdhZ2UuIEl0IGlzIGNhbGN1bGF0ZWQgYmFzZWQgb24gdGhlIG51bWJlciBvZiBhdmVyYWdlIGFkdWx0IHJlYWRzIHdvcmRzIGluIG9uZSBtaW51dGUuIE91ciBhcHByb2FjaCAgZ29hbCBpcyB0byBjYWxjdWxhdGUgaXQgdXNpbmcgZGF0YS5Db25zaWRlcmluZyB0aGUgZmluZGluZ3Mgb2YgdGhlIHByb2plY3QsIHRoZSByZWFkaW5nIHRpbWVzIHZhcnkgYWNjb3JkaW5nIHRvIHRoZSBkZW1vZ3JhcGhpYyBpbmZvcm1hdGlvbiBvZiB0aGUgcGVyc29uLGFuZCBhbHNvIHRoZSB2YXJpYWJsZXMgdGhhdCBhZmZlY3QgdGhlIHJlYWRpbmcgdGltZSBjaGFuZ2UgZm9yIGVhY2ggYXJ0aWNsZS4gCgoKCiMgMTAuIERlcGxveW1lbnQKClRoaXMgc2VjdGlvbiBhaW1zIHRvIHB1dCB0aGUgd2hvbGUgd29yayBpbnRvIHByYWN0aWNlLiBJdCBpcyBub3QgcG9zc2libGUgZm9yIHRoZSBzdHVkeSB0byByZWFjaCBpdHMgYnVzaW5lc3MgZ29hbCBpZiBhbGwgYW5hbHl6ZXMgcmVtYWluIGxvY2FsIGVudmlyb25tZW50LiBUaGVyZWZvcmUsIHRoZSBzdHVkeSBuZWVkcyB0byBiZSBpbnRlZ3JhdGVkIGluIHRoZSB0YXJnZXQgc3lzdGVtcy4gSW4gb3VyIHN0dWR5LCB3ZSB3aWxsIGRlcGxveSBvdXIgcmVwb3J0IHRvIEdvb2dsZSBGaXJlYmFzZS4KCgoK